<?php

namespace App\Services;

use App\Constants\FileCategoryEnum;
use App\Constants\FileStatusEnum;
use App\Constants\FileTypeEnum;
use App\Constants\StorageTypeEnum;
use App\Constants\UploadStatusEnum;
use App\Models\File;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

/**
 * 文件存储服务
 * 处理文件上传、存储、管理等核心功能
 */
class FileStorageService
{
    /**
     * 默认存储类型
     */
    private const DEFAULT_STORAGE_TYPE = StorageTypeEnum::LOCAL;

    /**
     * 默认文件分类
     */
    private const DEFAULT_FILE_CATEGORY = FileCategoryEnum::GENERAL;

    /**
     * 构造函数
     */
    public function __construct()
    {
        // 可以在这里注入其他依赖
    }

    /**
     * 上传文件
     */
    public function uploadFile(
        UploadedFile $uploadedFile,
        ?int $userId = null,
        ?string $category = null,
        ?string $storageType = null,
        ?array $options = []
    ): File {
        $userId = $userId ?? auth()->id();

        if (!$userId) {
            throw new \InvalidArgumentException('用户ID不能为空');
        }

        try {
            // 验证文件
            $this->validateFile($uploadedFile, $category);

            // 生成文件信息
            $fileInfo = $this->generateFileInfo($uploadedFile, $userId, $category, $storageType, $options);

            // 存储文件
            $filePath = $this->storeFile($uploadedFile, $fileInfo);

            // 创建文件记录
            $file = File::create(array_merge($fileInfo, [
                'file_path' => $filePath,
                'upload_status' => UploadStatusEnum::COMPLETED,
                'upload_progress' => 100,
            ]));

            // 异步处理文件（如果需要）
            if ($this->shouldProcessFile($file)) {
                $this->queueFileProcessing($file);
            }

            Log::info('文件上传成功', [
                'file_id' => $file->id,
                'user_id' => $userId,
                'file_name' => $file->original_name,
                'file_size' => $file->file_size,
            ]);

            return $file;

        } catch (\Exception $e) {
            Log::error('文件上传失败', [
                'user_id' => $userId,
                'file_name' => $uploadedFile->getClientOriginalName(),
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    /**
     * 批量上传文件
     */
    public function uploadFiles(
        array $uploadedFiles,
        ?int $userId = null,
        ?string $category = null,
        ?string $storageType = null,
        ?array $options = []
    ): array {
        $results = [];
        $errors = [];

        foreach ($uploadedFiles as $uploadedFile) {
            try {
                $file = $this->uploadFile($uploadedFile, $userId, $category, $storageType, $options);
                $results[] = $file;
            } catch (\Exception $e) {
                $errors[] = [
                    'file' => $uploadedFile->getClientOriginalName(),
                    'error' => $e->getMessage(),
                ];
            }
        }

        return [
            'success' => $results,
            'errors' => $errors,
        ];
    }

    /**
     * 下载文件
     */
    public function downloadFile(File $file, ?int $userId = null): array
    {
        // 检查访问权限
        $this->checkFileAccess($file, $userId);

        // 检查文件是否存在
        if (!$file->exists()) {
            throw new \Exception('文件不存在');
        }

        // 增加下载计数
        $file->incrementDownloadCount();

        // 获取文件内容或URL
        if ($this->shouldServeDirectly($file)) {
            return [
                'type' => 'content',
                'content' => $file->getContent(),
                'mime_type' => $file->mime_type,
                'file_name' => $file->original_name,
            ];
        } else {
            return [
                'type' => 'url',
                'url' => $file->url,
                'file_name' => $file->original_name,
            ];
        }
    }

    /**
     * 删除文件
     */
    public function deleteFile(File $file, ?int $userId = null): bool
    {
        // 检查删除权限
        $this->checkFileDeletePermission($file, $userId);

        try {
            // 删除物理文件
            $file->deleteFile();

            // 软删除记录
            $file->delete();

            Log::info('文件删除成功', [
                'file_id' => $file->id,
                'user_id' => $userId,
                'file_name' => $file->original_name,
            ]);

            return true;

        } catch (\Exception $e) {
            Log::error('文件删除失败', [
                'file_id' => $file->id,
                'user_id' => $userId,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    /**
     * 复制文件
     */
    public function copyFile(File $file, ?string $newName = null, ?int $userId = null): File
    {
        $userId = $userId ?? auth()->id();

        try {
            $newName = $newName ?? $this->generateCopyName($file->original_name);
            $newPath = $this->generateFilePath($file->file_type, $userId, $newName);

            // 复制物理文件
            $file->copyTo($newPath);

            // 创建新文件记录
            $newFile = File::create([
                'user_id' => $userId,
                'original_name' => $newName,
                'file_name' => basename($newPath),
                'file_path' => $newPath,
                'file_size' => $file->file_size,
                'file_type' => $file->file_type,
                'mime_type' => $file->mime_type,
                'file_hash' => $file->file_hash,
                'storage_type' => $file->storage_type,
                'file_category' => $file->file_category,
                'file_status' => FileStatusEnum::ACTIVE,
                'upload_status' => UploadStatusEnum::COMPLETED,
                'is_public' => $file->is_public,
                'created_by' => $userId,
            ]);

            Log::info('文件复制成功', [
                'original_file_id' => $file->id,
                'new_file_id' => $newFile->id,
                'user_id' => $userId,
            ]);

            return $newFile;

        } catch (\Exception $e) {
            Log::error('文件复制失败', [
                'file_id' => $file->id,
                'user_id' => $userId,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    /**
     * 移动文件
     */
    public function moveFile(File $file, string $newCategory, ?int $userId = null): bool
    {
        // 检查移动权限
        $this->checkFileMovePermission($file, $userId);

        try {
            $file->update([
                'file_category' => $newCategory,
                'updated_by' => $userId ?? auth()->id(),
            ]);

            Log::info('文件移动成功', [
                'file_id' => $file->id,
                'new_category' => $newCategory,
                'user_id' => $userId,
            ]);

            return true;

        } catch (\Exception $e) {
            Log::error('文件移动失败', [
                'file_id' => $file->id,
                'user_id' => $userId,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    /**
     * 重命名文件
     */
    public function renameFile(File $file, string $newName, ?int $userId = null): bool
    {
        // 检查重命名权限
        $this->checkFileRenamePermission($file, $userId);

        try {
            $file->update([
                'original_name' => $newName,
                'updated_by' => $userId ?? auth()->id(),
            ]);

            Log::info('文件重命名成功', [
                'file_id' => $file->id,
                'old_name' => $file->original_name,
                'new_name' => $newName,
                'user_id' => $userId,
            ]);

            return true;

        } catch (\Exception $e) {
            Log::error('文件重命名失败', [
                'file_id' => $file->id,
                'user_id' => $userId,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    /**
     * 获取文件列表
     */
    public function getFileList(
        ?int $userId = null,
        ?string $category = null,
        ?string $type = null,
        ?string $status = null,
        array $filters = [],
        int $page = 1,
        int $perPage = 20
    ): array {
        $query = File::query();

        // 权限过滤
        if ($userId) {
            $query->where('user_id', $userId);
        } elseif (!auth()->user()?->hasRole('admin')) {
            // 非管理员只能看到自己的文件或公开文件
            $query->where(function ($q) {
                $q->where('user_id', auth()->id())
                  ->orWhere('is_public', true);
            });
        }

        // 分类过滤
        if ($category) {
            $query->where('file_category', $category);
        }

        // 类型过滤
        if ($type) {
            $query->where('file_type', $type);
        }

        // 状态过滤
        if ($status) {
            $query->where('file_status', $status);
        }

        // 其他过滤器
        foreach ($filters as $key => $value) {
            switch ($key) {
                case 'search':
                    $query->where('original_name', 'like', "%{$value}%");
                    break;
                case 'size_min':
                    $query->where('file_size', '>=', $value);
                    break;
                case 'size_max':
                    $query->where('file_size', '<=', $value);
                    break;
                case 'date_from':
                    $query->where('created_at', '>=', $value);
                    break;
                case 'date_to':
                    $query->where('created_at', '<=', $value);
                    break;
                case 'tags':
                    $query->whereJsonContains('tags', $value);
                    break;
            }
        }

        // 排序
        $query->orderBy('created_at', 'desc');

        // 分页
        $files = $query->paginate($perPage, ['*'], 'page', $page);

        return [
            'files' => $files->items(),
            'pagination' => [
                'current_page' => $files->currentPage(),
                'last_page' => $files->lastPage(),
                'per_page' => $files->perPage(),
                'total' => $files->total(),
            ],
        ];
    }

    /**
     * 获取文件统计信息
     */
    public function getFileStatistics(?int $userId = null): array
    {
        $query = File::query();

        if ($userId) {
            $query->where('user_id', $userId);
        }

        $stats = $query->selectRaw('
            COUNT(*) as total_files,
            SUM(file_size) as total_size,
            COUNT(CASE WHEN file_status = ? THEN 1 END) as active_files,
            COUNT(CASE WHEN file_status = ? THEN 1 END) as inactive_files,
            COUNT(CASE WHEN is_public = 1 THEN 1 END) as public_files,
            SUM(download_count) as total_downloads
        ', [FileStatusEnum::ACTIVE, FileStatusEnum::INACTIVE])
        ->first();

        // 文件类型统计
        $typeStats = $query->selectRaw('file_type, COUNT(*) as count, SUM(file_size) as size')
            ->groupBy('file_type')
            ->get()
            ->keyBy('file_type');

        // 文件分类统计
        $categoryStats = $query->selectRaw('file_category, COUNT(*) as count')
            ->groupBy('file_category')
            ->get()
            ->keyBy('file_category');

        return [
            'total_files' => $stats->total_files ?? 0,
            'total_size' => $stats->total_size ?? 0,
            'total_size_formatted' => File::formatFileSize($stats->total_size ?? 0),
            'active_files' => $stats->active_files ?? 0,
            'inactive_files' => $stats->inactive_files ?? 0,
            'public_files' => $stats->public_files ?? 0,
            'total_downloads' => $stats->total_downloads ?? 0,
            'type_statistics' => $typeStats->toArray(),
            'category_statistics' => $categoryStats->toArray(),
        ];
    }

    // ============ 私有方法 ============

    /**
     * 验证文件
     */
    private function validateFile(UploadedFile $file, ?string $category = null): void
    {
        // 检查文件大小
        $maxSize = $this->getMaxFileSize($file->getMimeType(), $category);
        if ($file->getSize() > $maxSize) {
            throw new \Exception("文件大小超过限制，最大允许 {$this->formatBytes($maxSize)}");
        }

        // 检查文件类型
        if (!$this->isAllowedFileType($file->getMimeType(), $category)) {
            throw new \Exception('不支持的文件类型');
        }

        // 检查文件是否损坏
        if (!$file->isValid()) {
            throw new \Exception('文件上传失败：' . $file->getErrorMessage());
        }
    }

    /**
     * 生成文件信息
     */
    private function generateFileInfo(
        UploadedFile $file,
        int $userId,
        ?string $category = null,
        ?string $storageType = null,
        array $options = []
    ): array {
        $originalName = $file->getClientOriginalName();
        $mimeType = $file->getMimeType();
        $fileSize = $file->getSize();

        // 确定文件类型
        $fileType = FileTypeEnum::getTypeByMimeType($mimeType);

        // 确定文件分类
        $fileCategory = $category ?? $this->determineFileCategory($fileType, $originalName);

        // 确定存储类型
        $storageType = $storageType ?? $this->determineStorageType($fileCategory, $fileSize);

        // 生成文件名
        $fileName = $this->generateFileName($originalName, $userId);

        // 计算文件哈希
        $fileHash = hash_file('sha256', $file->getRealPath());

        return [
            'user_id' => $userId,
            'original_name' => $originalName,
            'file_name' => $fileName,
            'file_size' => $fileSize,
            'file_type' => $fileType,
            'mime_type' => $mimeType,
            'file_hash' => $fileHash,
            'storage_type' => $storageType,
            'file_category' => $fileCategory,
            'file_status' => FileStatusEnum::ACTIVE,
            'is_public' => $options['is_public'] ?? false,
            'metadata' => $this->extractFileMetadata($file),
            'tags' => $options['tags'] ?? [],
            'expires_at' => $options['expires_at'] ?? null,
            'created_by' => auth()->id(),
        ];
    }

    /**
     * 存储文件
     */
    private function storeFile(UploadedFile $file, array $fileInfo): string
    {
        $filePath = $this->generateFilePath(
            $fileInfo['file_type'],
            $fileInfo['user_id'],
            $fileInfo['file_name']
        );

        // 存储文件
        $stored = Storage::disk($fileInfo['storage_type'])->put($filePath, file_get_contents($file->getRealPath()));

        if (!$stored) {
            throw new \Exception('文件存储失败');
        }

        return $filePath;
    }

    /**
     * 生成文件名
     */
    private function generateFileName(string $originalName, int $userId): string
    {
        $extension = pathinfo($originalName, PATHINFO_EXTENSION);
        $basename = pathinfo($originalName, PATHINFO_FILENAME);

        // 生成唯一文件名
        $timestamp = now()->format('Ymd_His');
        $random = Str::random(8);

        return "{$userId}_{$timestamp}_{$random}_{$basename}." . strtolower($extension);
    }

    /**
     * 生成文件路径
     */
    private function generateFilePath(string $fileType, int $userId, string $fileName): string
    {
        $year = now()->format('Y');
        $month = now()->format('m');
        $day = now()->format('d');

        // 按类型和时间组织目录结构
        return "uploads/{$fileType}/{$year}/{$month}/{$day}/{$userId}/{$fileName}";
    }

    /**
     * 确定文件分类
     */
    private function determineFileCategory(string $fileType, string $fileName): string
    {
        // 根据文件名关键词判断分类
        $name = strtolower($fileName);

        if (str_contains($name, ['avatar', '头像'])) {
            return FileCategoryEnum::AVATAR;
        }

        if (str_contains($name, ['contract', '合同', 'agreement'])) {
            return FileCategoryEnum::CONTRACT;
        }

        if (str_contains($name, ['resume', '简历', 'cv'])) {
            return FileCategoryEnum::RESUME;
        }

        // 根据文件类型判断默认分类
        if (FileTypeEnum::isImage($fileType)) {
            return FileCategoryEnum::GENERAL;
        }

        if (FileTypeEnum::isDocument($fileType)) {
            return FileCategoryEnum::EMPLOYEE_DOC;
        }

        return self::DEFAULT_FILE_CATEGORY;
    }

    /**
     * 确定存储类型
     */
    private function determineStorageType(string $category, int $fileSize): string
    {
        // 大文件使用云存储
        if ($fileSize > 100 * 1024 * 1024) { // 100MB
            return StorageTypeEnum::QINIU_KODO; // 推荐七牛云
        }

        // 敏感文件使用私有存储
        if (FileCategoryEnum::isSensitive($category)) {
            return StorageTypeEnum::LOCAL;
        }

        return self::DEFAULT_STORAGE_TYPE;
    }

    /**
     * 获取最大文件大小
     */
    private function getMaxFileSize(string $mimeType, ?string $category = null): int
    {
        $fileType = FileTypeEnum::getTypeByMimeType($mimeType);
        return FileTypeEnum::getMaxSize($fileType);
    }

    /**
     * 检查是否为允许的文件类型
     */
    private function isAllowedFileType(string $mimeType, ?string $category = null): bool
    {
        $fileType = FileTypeEnum::getTypeByMimeType($mimeType);

        // 如果指定了分类，检查是否支持该文件类型
        if ($category) {
            $supportedTypes = FileCategoryEnum::getSupportedTypes($category);
            if ($supportedTypes !== ['*'] && !in_array($fileType, $supportedTypes)) {
                return false;
            }
        }

        // 检查文件类型是否在允许列表中
        $allowedTypes = config('files.allowed_types', ['*']);
        return $allowedTypes === ['*'] || in_array($fileType, $allowedTypes);
    }

    /**
     * 提取文件元数据
     */
    private function extractFileMetadata(UploadedFile $file): array
    {
        $metadata = [];

        // 基本信息
        $metadata['client_name'] = $file->getClientOriginalName();
        $metadata['client_mime'] = $file->getClientMimeType();
        $metadata['client_size'] = $file->getClientSize();

        // 如果是图片，提取图片信息
        if (FileTypeEnum::isImage(FileTypeEnum::getTypeByMimeType($file->getMimeType()))) {
            $imageInfo = getimagesize($file->getRealPath());
            if ($imageInfo) {
                $metadata['width'] = $imageInfo[0];
                $metadata['height'] = $imageInfo[1];
                $metadata['image_type'] = $imageInfo[2];
                $metadata['bits'] = $imageInfo['bits'] ?? null;
                $metadata['channels'] = $imageInfo['channels'] ?? null;
            }
        }

        return $metadata;
    }

    /**
     * 检查文件访问权限
     */
    private function checkFileAccess(File $file, ?int $userId = null): void
    {
        $userId = $userId ?? auth()->id();

        // 公开文件允许任何人访问
        if ($file->is_public) {
            return;
        }

        // 检查是否为文件所有者
        if ($file->user_id === $userId) {
            return;
        }

        // 检查是否有管理员权限
        if (auth()->user()?->hasRole('admin')) {
            return;
        }

        throw new \Exception('没有访问该文件的权限');
    }

    /**
     * 检查文件删除权限
     */
    private function checkFileDeletePermission(File $file, ?int $userId = null): void
    {
        $userId = $userId ?? auth()->id();

        // 检查是否为文件所有者
        if ($file->user_id !== $userId && !auth()->user()?->hasRole('admin')) {
            throw new \Exception('没有删除该文件的权限');
        }
    }

    /**
     * 检查文件移动权限
     */
    private function checkFileMovePermission(File $file, ?int $userId = null): void
    {
        $this->checkFileDeletePermission($file, $userId);
    }

    /**
     * 检查文件重命名权限
     */
    private function checkFileRenamePermission(File $file, ?int $userId = null): void
    {
        $this->checkFileDeletePermission($file, $userId);
    }

    /**
     * 判断是否应该直接提供文件内容
     */
    private function shouldServeDirectly(File $file): bool
    {
        // 小文件可以直接提供内容
        return $file->file_size < 10 * 1024 * 1024; // 10MB以下
    }

    /**
     * 判断是否需要处理文件
     */
    private function shouldProcessFile(File $file): bool
    {
        // 图片文件需要生成缩略图
        if ($file->is_image) {
            return true;
        }

        // 视频文件需要转码
        if ($file->is_video) {
            return true;
        }

        // 大文档需要生成预览
        if ($file->is_document && $file->file_size > 1024 * 1024) { // 1MB以上
            return true;
        }

        return false;
    }

    /**
     * 队列文件处理任务
     */
    private function queueFileProcessing(File $file): void
    {
        // 这里可以实现异步文件处理队列
        // 例如：生成缩略图、视频转码、文档预览等

        Log::info('文件处理任务已加入队列', [
            'file_id' => $file->id,
            'file_type' => $file->file_type,
        ]);
    }

    /**
     * 生成复制文件名
     */
    private function generateCopyName(string $originalName): string
    {
        $info = pathinfo($originalName);
        $name = $info['filename'];
        $extension = $info['extension'];

        return "{$name}_副本.{$extension}";
    }

    /**
     * 格式化字节数
     */
    private function formatBytes(int $bytes): string
    {
        return File::formatFileSize($bytes);
    }
}
